// File [test/pointers/notifying_ptr/01/main.cpp]

#include <iostream>
#include <ostream>
#include <alfs/pointers/NotifyingPtr.hpp>

void noNotification() {}


class BaseClass: public alfs::destructionEvent::Source {};

class Derived: public BaseClass {};

int testAutoDestroy_nDestroyCalls = 0;
namespace std{
    template<>
    struct default_delete<Derived>
    {
        void operator()( Derived* p ) const
        {
            std::cout << "Destroyed (standard)" << std::endl;
            delete p;
            ++testAutoDestroy_nDestroyCalls;
        }
    };
}  // std

void testAutoDestroy()
{
    using namespace alfs;
    std::cout << "\n* testAutoDestroy:" << std::endl;
    {
        NotifyingPtr<Derived>::Auto     p( &noNotification, newNoArgs );
        NotifyingPtr<BaseClass>         q = p;
#ifdef  SHOULD_FAIL_DERIVED_FROM_BASE
        NotifyingPtr<Derived>           r = q;      // Should not compile.
#endif
    }
    (testAutoDestroy_nDestroyCalls == 1) || throwX( "Failed, destroy count != 1" );
    std::cout << "Passed testAutoDestroy" << std::endl;
}

class Naughty
{
public:
    virtual ~Naughty() {}
    double pi() const { return 3.14; }
    void selfDestroy() { delete this; }
};

void testAccessCheck()
{
    using namespace alfs;
    std::cout << "\n* testAccessCheck:" << std::endl;
    bool stdExceptionExpected = false;
    try
    {
        NotifyingPtr<Naughty>::Auto p( &noNotification, newNoArgs );

        p->pi();
        std::cout << "Good, access of valid pointer succeeded" << std::endl;
        p->selfDestroy();
        stdExceptionExpected = true;  p->pi();
        throw 666;
    }
    catch( int )
    {
        throwX( "Failed testAccessCheck" );
    }
    catch( std::exception const& )
    {
        if( !stdExceptionExpected ) { throw; }
        std::cout << "Good, access of invalid pointer failed" << std::endl;
    }
    std::cout << "Passed testAccessCheck" << std::endl;
}

struct WildOne { virtual ~WildOne() {} };
typedef alfs::WithDestructionEventWrapper<WildOne>::Type    WildOneX;

unsigned nNotifications     = 0;

void onNotify() { ++nNotifications; }

void testNotifications()
{
    using namespace alfs;
    std::cout << "\n* testNotifications:" << std::endl;

    // Neg-test: no notification for automatic deletion, please:
    {
        NotifyingPtr<WildOne>::Auto   p( &onNotify, newNoArgs );
    }
    (::nNotifications == 0) || throwX( "Failed neg-test, nr. of notifications > 0" );

    // Pos-test: notification of external deletion, please:
    {
        WildOneX*               pRaw = new WildOneX;
        NotifyingPtr<WildOne>   p( &onNotify, pRaw );

        assert( ::nNotifications == 0 );

        delete pRaw;
        assert( p.isVoid() );
    }
    (::nNotifications == 1) || throwX( "Failed pos-test, nr. of notifications != 1" );

    std::cout << "Passed testNotifications" << std::endl;
}

int main()
{
    static char const testName[] = "NotifyingPtr test 01";
    using namespace alfs;
    std::cout << testName << std::endl;
    try
    {
        testAutoDestroy();
        testAccessCheck();
        testNotifications();
        //testDynamicConversion();

        std::cout << "\nPassed all tests." << std::endl;
        return EXIT_SUCCESS;
    }
    catch( std::exception const& x )
    {
        std::cout << "Failed" << std::endl;
        std::cerr << "!" << x.what() << std::endl;
        return EXIT_FAILURE;
    }
}
